Skip to main content

Grating Coupler

Introduction:

Grating coupling is a simple method for connecting photonic integrated chips with optical fibers.

Simulation

1 Code Description

1.1 Import Toolkit

First, we need to import maxoptics_sdk and Python's third-party package. The import module for FDE and FDTD simulation are shown below.

import maxoptics_sdk.all as mo
from maxoptics_sdk.helper import timed, with_path
import os
import time
import numpy as np
from typing import NamedTuple

1.2 Define Simulation Function and parameters

To facilitate parameter changes, we can define function to encapsulate the entire simulation project. Before starting the simulation, you can define variables to control the parameters. Set as follows.

@timed
@with_path
def simulation(*,wavelength,wavelength_span,grids_per_lambda,run_options: 'RunOptions',**kwargs):
# region --- General Parameters ---
waveform_name = f'wv{wavelength*1e3}'
path = kwargs['path']
simu_name = 'GratingCoupler'
time_str = time.strftime('%Y%m%d_%H%M%S', time.localtime())
project_name = f'{simu_name}_{time_str}'
plot_path = f'{path}/plots/{project_name}/'
gds_file_root_path = os.path.abspath(os.path.join(path, '..'))
gds_file = gds_file_root_path + '/examples_gds/gc.gds'
# endregion

1.3 Create project

You can create a new project using the Project function of Max's software development toolkit.

# region --- Project ---
pj = mo.Project(name=project_name)
# endregion

1.4 Add Material

Here we demonstrate using the Material function to create material and using the add_lib function to add materials from the material library. You can refer to the following script to set material.

# region --- Material ---
mt = pj.Material()
mt.add_nondispersion(name='Si', data=[(3.47656, 0)], order=2)
mt.add_nondispersion(name='SiO2', data=[(1.44402, 0)], order=2)
mt.add_lib(name='Air', data=mo.Material.Air, order=2)
# endregion

The name is used to define the name of the added material.
The data is used to receive refractive index data extracted from the material library.
The order is used to set the material priority of the grid.

1.5 Add waveform

Adding a light source for simulating in 3D FDTD, and we use Waveform to set the waveform parameters of the light source.

# region --- Waveform ---
wv = pj.Waveform()
wv.add(name=waveform_name,type='gaussian_waveform',
property={'set': 'frequency_wavelength', # selections are ['frequency_wavelength','time_domain']
'set_frequency_wavelength': {
'range_type': 'wavelength', # selections are ['frequency','wavelength']
'range_limit': 'center_span', # selections are ['min_max','center_span']
'wavelength_center': wavelength,
'wavelength_span': wavelength_span,},})
# endregion

name sets the name of the waveform, wavelength_center sets the center wavelength of the light source, and wavelength_span sets the wavelength range of the light source.

1.6 Add Structure

Microring resonator is typical filter, including two straight optical waveguides and a coupled ring-shaped waveguide.

We use Structure to create structure , where mesh_type is the type of mesh, mesh_factor is the growth factor of the mesh, and background_material is the background material of the structure. Use the add_geometry function to add geometric structures and select the type from the structural components. The specific properties can be set as follows.

# region --- Structure ---
st = pj.Structure()
st.add_geometry(name='top_cladding',type='gds_file',
property={'general': { 'path': gds_file, 'cell_name': 'gc', 'layer_name': (1, 0)},
'geometry': {'z': 0.11, 'z_span': 0.22},
'material': {'material': mt['Si'], 'mesh_order': 2}})
st.add_geometry(name='middle_cladding',type='gds_file',
property={'general': {'path': gds_file, 'cell_name': 'gc', 'layer_name': (2, 0)},
'geometry': {'z': 0.075, 'z_span': 0.15},
'material': {'material': mt['Si'], 'mesh_order': 2}})
st.add_geometry(name='grating',type='gds_file',
property={'general': {'path': gds_file, 'cell_name': 'gc', 'layer_name': (3, 0)},
'geometry': {'z': 0.185, 'z_span': 0.07},
'material': {'material': mt['Si'], 'mesh_order': 2}})
st.add_geometry(name='SiO2_TOX_UP',type='gds_file',
property={'general': {'path': gds_file, 'cell_name': 'gc', 'layer_name': (4, 0)},
'geometry': {'z': 0.5, 'z_span': 1.0},
'material': {'material': mt['SiO2'], 'mesh_order': 1}})
st.add_geometry(name='SiO2_TOX_DOWN',type='gds_file',
property={'general': {'path': gds_file, 'cell_name': 'gc', 'layer_name': (5, 0)},
'geometry': {'z': -1, 'z_span': 2},
'material': {'material': mt['SiO2'], 'mesh_order': 2}})
st.add_geometry(name='Si_substrate',type='gds_file',
property={'general': {'path': gds_file, 'cell_name': 'gc', 'layer_name': (6, 0)},
'geometry': {'z': -6, 'z_span': 8},
'material': {'material': mt['Si'], 'mesh_order': 2}})
# endregion
KeyValuetypeDescription
nametop_claddingstringname the added geometry
typegds_filestringselect the type of structure
x0floatcenter position in the x-direction of the geometric structure 
pathgds_filestringfile path of GDS file
cell_nameSSCstringname of the GDS cell
layer_name(1,0)listname of the GDS layer
z0.11floatcenter position in the z-direction of the geometric structure
z_span0.22floatlength of the geometric structure in the z-direction
materialmt["Si"]materialselect the material added Materials
mesh_order2integerset the priority of the material

1.7 Add Simulation

We use the `Simulation` function to create a simulation and the `add` function to add a solver.
# region --- Simulation ---
simu = pj.Simulation()
simu.add(name=simu_name, type="FDTD",
property={"background_material": mt["Air"],
"geometry": {'x': -3.5, 'x_span': 47,'y': 0, 'y_span': 28,'z': -0.5, 'z_span': 5},
"boundary_conditions": {"x_min_bc": "PML", "x_max_bc": "PML", "y_min_bc": "PML", "y_max_bc": "PML", "z_min_bc": "PML", "z_max_bc": "PML",
"pml_settings": {"all_pml": {"layers": 8, "kappa": 2, "sigma": 0.8, "polynomial": 3, "alpha": 0, "alpha_polynomial": 1, }}},
"general": {'simulation_time': 30000, },
"mesh_settings": {"mesh_factor": 1.2,"mesh_type": "auto_non_uniform",
"mesh_accuracy": {"cells_per_wavelength": grids_per_lambda},"minimum_mesh_step_settings": {"min_mesh_step": 1e-4},
"mesh_refinement": {"mesh_refinement": "curve_mesh",}},})
# endregion

1.8 Add source

In 3D FDTD simulation, a light source is required. We use `Source` to create the light source and `add` to add the required light source. The settings for the light source as follows.
# region --- GaussianSource ---
so = pj.Source()
so.add(name='source',type='gaussian_source',
property={'general': {'angle_theta': 12,'angle_phi': 0,'polarization_angle': 90,
"waveform": {"waveform_id": wv[waveform_name]}, "inject_axis": "x_axis", "direction": "forward",
'beam_settings': {'beam_parameters': "waist_size_and_position", # [waist_size_and_position,beam_size_and_divergence]
'waist_radius': 5.2,
'distance_from_waist': 1.5,}},
'geometry': {'x': 4, 'x_span': 20, 'y': 0, 'y_span': 20, 'z': 1.5, 'z_span': 0}})
# endregion

Use type to select the type of light source, and axis to set the direction of the mode light source.

1.9 Add Monitor

In the simulation, Monitor function is used to create monitor and add function is used to add a monitor. By using type to select a power monitor, the transmittance and field distribution of the cross-section can be obtained. It is necessary to add a time monitor to check the field strength at the end of the simulation to determine the accuracy of the simulation results.

# region --- Monitor ---
mn = pj.Monitor()
mn.add(name='Global Option', type='global_option',
property={'frequency_power': {'spacing_type': 'wavelength', 'spacing_limit': 'center_span',
'wavelength_center': wavelength, 'wavelength_span': wavelength_span, 'frequency_points': 5}})
mn.add(name='x_normal', type='power_monitor',
property={'general': {'frequency_profile': {'spacing_type': 'wavelength', 'spacing_limit': 'center_span',
'wavelength_center': wavelength, 'wavelength_span': wavelength_span, 'frequency_points': 101}},
'geometry': {'monitor_type': '2d_x_normal', 'x': -26, 'x_span': 0, 'y': 0, 'y_span': 3, 'z': 0.11, 'z_span': 3}})
mn.add(name='y_normal', type='power_monitor',
property={'geometry': {'monitor_type': '2d_y_normal', 'x': -5, 'x_span': 44, 'y': 0, 'y_span': 0, 'z': 0.2, 'z_span': 3.5}})
# endregion

1.10 Run

Pass in the name of the simulation and use simu[simu_name].run function to run the simulation.We support the GPU acceleration for FDTD simlation.

# region --- Run ---
if run_options.run:
fdtd_res = simu[simu_name].run(resources={"compute_resources": "gpu", "gpu_devices": [{"id": 0},{"id": 1},{"id": 2},{"id": 3}]})
# endregion
# endregion

1.11 Run and Extract Results

Extract data using extract, where data is the calculation result data, savepath is the storage path, target is the classification of the data, and monitor_name is the name of the monitor. The data extraction reference is as follows.

# region --- See Results ---
if run_options.extract and run_options.run:
fdtd_res.extract(data='fdtd:power_monitor',savepath=f'{plot_path}02_x_normal_abs(T)',
monitor_name='x_normal',attribute='T',target='line',plot_x='wavelength',real=True,imag=True,export_csv=True,show=False)
fdtd_res.extract(data='fdtd:power_monitor',savepath=f'{plot_path}02_y_normal_E_{wavelength}_um',
monitor_name='y_normal',target="intensity",attribute="E",real=True,imag=False,
wavelength=f"{wavelength}",plot_x="x",plot_y="z",show=False,export_csv=True,)
# endregion
return fdtd_res if run_options.run else None

1.12 Control Switch

We can control the operation of the simulation by passing in bool values through tuple, as shown in the following code. In every simulation, only one solver will be enabled. When using the FDE solver, set "run_fde" to True and "run_fdtd" to False; When using the FDTD solver, set "run_fdtd" to True and "run_fde" to False.

class RunOptions(NamedTuple):
index_preview: bool
run: bool
extract: bool

if __name__ == '__main__':
simulation(wavelength=1.55, wavelength_span=0.1, grids_per_lambda=8,
run_options=RunOptions(index_preview=True, run=True, extract=True))

2. Output results

Electric profile

When the wavelength of the incident light meets the Bragg condition, the incident light will enter the waveguide through grating coupling.

Transmission

The transmittance of grating coupling varies with wavelength as shown in the following figure.

References

D. Taillaert, F. Van Laere, M. Ayre, W. Bogaerts, D. Van Thourhout, P. Bienstman and R. Baets, “Grating Couplers for Coupling between Optical Fibers and Nanophotonic Waveguides,” Japanese Journal of Applied Physics, vol. 45, no. 8a, pp. 6071-6077, 2006.